package com.sojson.config.datasource.bean.impl;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.springframework.context.annotation.Configuration;

import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.filter.stat.StatFilter;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.wall.WallConfig;
import com.sojson.config.datasource.bean.DbPro;
import com.sojson.constant.Constant;
import com.sojson.util.text.Convert;

/**
 * Druid数据库配置属性
 * 
 * @author liu
 * @date 2021-07-04
 */
@Configuration
public class DbDruidPro extends DbPro {

    /***/
    private static final long serialVersionUID = 1L;

    /** 初始化大小 */
    private static final int INITIAL_SIZE =
        Convert.toInt(Constant.PROPERTIES.getProperty("spring.datasource.druid.testOnReturn"), 5);
    /** 初始化最小 */
    private static final int MIN_IDLE =
        Convert.toInt(Constant.PROPERTIES.getProperty("spring.datasource.druid.minIdle"), 10);
    /** 初始化最大 */
    private static final int MAX_ACTIVE =
        Convert.toInt(Constant.PROPERTIES.getProperty("spring.datasource.druid.maxActive"), 20);
    /** 配置获取连接等待超时的时间 */
    private static final int MAX_WAIT =
        Convert.toInt(Constant.PROPERTIES.getProperty("spring.datasource.druid.maxWait"), 60000);
    /** 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位是毫秒 */
    private static final int TIME_BETWEEN_EVICTION_RUNS_MILLIS =
        Convert.toInt(Constant.PROPERTIES.getProperty("spring.datasource.druid.timeBetweenEvictionRunsMillis"), 60000);
    /** 配置一个连接在池中最小生存的时间，单位是毫秒 */
    private static final int MIN_EVICTABLE_IDLE_TIME_MILLIS =
        Convert.toInt(Constant.PROPERTIES.getProperty("spring.datasource.druid.minEvictableIdleTimeMillis"), 300000);
    /** 配置一个连接在池中最大生存的时间，单位是毫秒 */
    private static final int MAX_EVICTBALE_IDEL_TIME_MILLIS =
        Convert.toInt(Constant.PROPERTIES.getProperty("spring.datasource.druid.maxEvictableIdleTimeMillis"), 900000);
    private static final String VALIDATION_QUERY =
        Convert.toStr(Constant.PROPERTIES.getProperty("spring.datasource.druid.validationQuery"), "SELECT 1 FROM DUAL");
    private static final boolean TEST_WHILE_IDLE =
        Convert.toBool(Constant.PROPERTIES.getProperty("spring.datasource.druid.testWhileIdle"), true);
    private static final boolean TEST_ON_BORROW =
        Convert.toBool(Constant.PROPERTIES.getProperty("spring.datasource.druid.testOnBorrow"), false);
    private static final boolean TEST_ON_RETURN =
        Convert.toBool(Constant.PROPERTIES.getProperty("spring.datasource.druid.testOnReturn"), false);
    /** 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 */
    private static final String FILTERS =
        Convert.toStr(Constant.PROPERTIES.getProperty("spring.datasource.druid.filters"), "stat, wall, log4j");

    /** SQL记录,状态监控 */
    private static final boolean FILTER_STAT_ENABLED =
        Convert.toBool(Constant.PROPERTIES.getProperty("spring.datasource.druid.filter.stat.enabled"), true);
    private static final boolean FILTER_STAT_LOG_SLOW_SQL =
        Convert.toBool(Constant.PROPERTIES.getProperty("spring.datasource.druid.filter.stat.log-slow-sql"), true);
    private static final long FILTER_STAT_SLOW_SQL_MILLIS =
        Convert.toLong(Constant.PROPERTIES.getProperty("spring.datasource.druid.filter.stat.slow-sql-millis"), 1000L);
    private static final boolean FILTER_STAT_MERGE_SQL =
        Convert.toBool(Constant.PROPERTIES.getProperty("spring.datasource.druid.filter.stat.merge-sql"), true);
    /** 开启一次支持执行多条sql */
    private static final boolean FILTER_CONFIG_MULTI_STATEMENT_ALLOW = Convert.toBool(
        Constant.PROPERTIES.getProperty("spring.datasource.druid.filter.wall.config.multi-statement-allow"), true);
    /** 合并多个DruidDataSource的监控数据 */
    private static final boolean USE_GLOBAL_DATA_SOURCE_STAT =
        Convert.toBool(Constant.PROPERTIES.getProperty("spring.datasource.druid.use-global-data-source-stat"), true);
    /** 打开PSCache，并且指定每个连接上PSCache的大小 */
    private static final boolean POLL_PREPARED_STATMENTS =
        Convert.toBool(Constant.PROPERTIES.getProperty("spring.datasource.druid.pool-prepared-statements"), true);
    private static final int MAX_POLL_PREPARED_STATMENT_PER_CONNECTION_SIZE = Convert.toInt(
        Constant.PROPERTIES.getProperty("spring.datasource.druid.max-pool-prepared-statement-per-connection-size"), 20);

    /**
     * 初始化
     * 
     * @param datasource
     * @return
     * @throws SQLException
     */
    public DruidDataSource init(DruidDataSource datasource) throws SQLException {
        datasource = dataSource(datasource);
        datasource = filter(datasource);
        return datasource;
    }

    public DruidDataSource dataSource(DruidDataSource datasource) throws SQLException {
        datasource.setUrl(getUrl());
        datasource.setUsername(getUsername());
        datasource.setPassword(getPassword());

        /** 配置初始化大小、最小、最大 */
        datasource.setInitialSize(INITIAL_SIZE);
        datasource.setMaxActive(MAX_ACTIVE);
        datasource.setMinIdle(MIN_IDLE);

        /** 配置获取连接等待超时的时间 */
        datasource.setMaxWait(MAX_WAIT);

        /** 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位是毫秒 */
        datasource.setTimeBetweenEvictionRunsMillis(TIME_BETWEEN_EVICTION_RUNS_MILLIS);

        /** 配置一个连接在池中最小、最大生存的时间，单位是毫秒 */
        datasource.setMinEvictableIdleTimeMillis(MIN_EVICTABLE_IDLE_TIME_MILLIS);
        datasource.setMaxEvictableIdleTimeMillis(MAX_EVICTBALE_IDEL_TIME_MILLIS);

        /**
         * 用来检测连接是否有效的sql，要求是一个查询语句，常用select 'x'。如果validationQuery为null，testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
         */
        datasource.setValidationQuery(VALIDATION_QUERY);
        /** 建议配置为true，不影响性能，并且保证安全性。申请连接的时候检测，如果空闲时间大于timeBetweenEvictionRunsMillis，执行validationQuery检测连接是否有效。 */
        datasource.setTestWhileIdle(TEST_WHILE_IDLE);
        /** 申请连接时执行validationQuery检测连接是否有效，做了这个配置会降低性能。 */
        datasource.setTestOnBorrow(TEST_ON_BORROW);
        /** 归还连接时执行validationQuery检测连接是否有效，做了这个配置会降低性能。 */
        datasource.setTestOnReturn(TEST_ON_RETURN);

        /** 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 */
        datasource.setFilters(FILTERS);

        /** 打开PSCache，并且指定每个连接上PSCache的大小 */
        datasource.setPoolPreparedStatements(POLL_PREPARED_STATMENTS);
        datasource.setMaxPoolPreparedStatementPerConnectionSize(MAX_POLL_PREPARED_STATMENT_PER_CONNECTION_SIZE);

        /** 合并多个DruidDataSource的监控数据 */
        datasource.setUseGlobalDataSourceStat(USE_GLOBAL_DATA_SOURCE_STAT);
        return datasource;
    }

    public DruidDataSource filter(DruidDataSource datasource) {
        if (!FILTER_STAT_ENABLED) {
            return datasource;
        }

        List<Filter> arrayList = new ArrayList<>();
        StatFilter statFilter = new StatFilter();
        arrayList.add(statFilter);
        datasource.setProxyFilters(arrayList);
        WallConfig wallConfig = new WallConfig();
        wallConfig.setMultiStatementAllow(FILTER_CONFIG_MULTI_STATEMENT_ALLOW);

        statFilter.setLogSlowSql(FILTER_STAT_LOG_SLOW_SQL);
        statFilter.setSlowSqlMillis(FILTER_STAT_SLOW_SQL_MILLIS);
        statFilter.setMergeSql(FILTER_STAT_MERGE_SQL);

        return datasource;
    }

}